#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include <dirent.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


#define SESSION_DIR     "/sys/kernel/debug/amvx_if0/session"
#define PARSE_FAILED    (-1)

typedef struct {
    char width[8];
    char height[8];
    char bufmin[8];
    char bufcnt[8];
    char format[16];
} port_t;

typedef struct {
    uint32_t id;
    char codec[16];
    char name[18];
    port_t port[2];
} session_t;


uint32_t session_name_to_id(char *name)
{
    uint32_t id = 0;
    int i = 0;
    while (name[i] != '\0') {
        id = id << 4;
        id |= (name[i] < 'a') ? name[i] - '0' : name[i] - 'a' + 0xa;
        i++;
    }
    return id;
}

static inline void fourcc_to_format(char *fourcc, char *format)
{
    const struct {
        char *fourcc; // like: 0x3231564e => "21VN"
        char *format; // like: nv12
    } table[] = {
        {"0x3231564e", "NV12"},
        {"0x34363248", "H264"},
        {"0x35363248", "H265"},
        {"0x4745504a", "JPEG"},
        {NULL, NULL},
    };

    for (int i = 0; table[i].fourcc != NULL; i++) {
        if (strcmp(fourcc, table[i].fourcc) == 0) {
            strcpy(format, table[i].format);
            return;
        }
    }

    strcpy(format, fourcc);
}

void parse_port(port_t *port, int dir, char *session_name)
{
    char stat[128] = {0};
    char buf[1024] = {0};

    sprintf(stat, "%s/%s/port%d/stat", SESSION_DIR, session_name, dir);

    int fd = open(stat, O_RDONLY);
    if (fd < 0) {
        printf("open failed: %s\n", stat);
        return;
    }

    int len = read(fd, buf, sizeof(buf));
    if (len <= 0) {
        printf("read: %s failed\n", stat);
        return;
    }

    close(fd);

    char *ptr = buf, *line, *word;
    while ((line = strsep(&ptr, "\n")) != NULL) {
        char *key = NULL, *val = NULL;
        while ((word = strsep(&line, " :")) != NULL) {
            if (word[0] == '\0' || word[0] == ' ' || word[0] == ':')
                continue;

            if (key == NULL) {
                key = word;
                continue;
            }

            if (val == NULL) {
                val = word;
                break;
            }
        }

        if (key == NULL || val == NULL)
            continue;

        // line
        if (strcmp(key, "width") == 0) {
            strcpy(port->width, val);
            continue;
        }
        if (strcmp(key, "height") == 0) {
            strcpy(port->height, val);
            continue;
        }
        if (strcmp(key, "buffer_min") == 0) {
            strcpy(port->bufmin, val);
            continue;
        }
        if (strcmp(key, "buffer_count") == 0) {
            strcpy(port->bufcnt, val);
            continue;
        }
        if (strcmp(key, "pixelformat") == 0) {
            fourcc_to_format(val, port->format);
            continue;
        }
    }
}

void parse_codec(session_t *session, char *session_name)
{
    char buf[4] = {0};
    char path[128] = {0};

    sprintf(path, "%s/%s/workload/codec", SESSION_DIR, session_name);

    int fd = open(path, O_RDONLY);
    if (fd < 0) {
        goto failed;
    }
    int len = read(fd, buf, sizeof(buf));
    if (len <= 0) {
        printf("read: %s failed\n", path);
        goto failed;
    }
    close(fd);
    if (strcmp(buf, "1\n") == 0) {
        sprintf(session->codec, "%s-%s", session->port[0].format, "DEC");
        // strcpy(codec, "dec");
    } else if (strcmp(buf, "2\n") == 0) {
        sprintf(session->codec, "%s-%s", session->port[1].format, "ENC");
        // strcpy(codec, "enc");
    } else {
        goto failed;
    }
    return;

failed:
    strcpy(session->codec, "xxxx");
}

static inline void print_session(session_t *s)
{
    printf("%s | %s | %s %s %s %s %s | %s %s %s %s %s\n", \
            s->name, s->codec, \
            s->port[0].format, s->port[0].width, s->port[0].height, s->port[0].bufmin, s->port[0].bufcnt, \
            s->port[1].format, s->port[1].width, s->port[1].height, s->port[1].bufmin, s->port[1].bufcnt  \
          );
}

void parse_session(char *name)
{
    session_t session = {0};

    session.id = session_name_to_id(name);
    strcpy(session.name, name);

    for (int i = 0; i < 2; i++) {
        parse_port(&session.port[i], i, name);
    }

    parse_codec(&session, name);

    print_session(&session);
}

int main()
{
    DIR *dir;
    struct dirent *entry;

    dir = opendir(SESSION_DIR);
    if (dir == NULL) {
        printf("can't open session dir\n");
        return 1;
    }

    char *name;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_DIR) {
            name = entry->d_name;
            if (name[0] == '.') // ignore . or .. dir
                continue;
            parse_session(name);
        }
    }

    closedir(dir);
    return 0;
}
